package frame.system.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import frame.base.BaseConstant;
import frame.base.BaseService;
import frame.base.BootstrapTable;
import frame.base.Typeahead;
import frame.system.mapper.UserMapper;
import frame.system.qo.AuthQo;
import frame.system.to.User;
import frame.util.shiro.LoginUser;
import frame.util.shiro.UserMD5;
import frame.util.shiro.UserRealm;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.StringUtil;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserService extends BaseService<Integer, User, AuthQo> {

    @Autowired
    private UserMapper userMapper;

    @Override
    protected Mapper<User> mainMapper() {
        return userMapper;
    }

    private static int maxError = BaseConstant.PASSWORD_ERROR_LIMIT;

    public BootstrapTable<User> pageList(final AuthQo qo) {
        //分页查询
        Page<User> page = PageHelper.startPage(qo.page(), qo.size()).doSelectPage(() -> {
            userMapper.pageList(qo);
        });
        return new BootstrapTable<User>(page.getTotal(),page.getResult());
    }

    public List<Typeahead<Integer>> typeahead(AuthQo qo) {
        List<Typeahead<Integer>> types = new ArrayList<>();
        Example example = new Example(User.class);
        example.selectProperties("id","fullName");
        Example.Criteria criteria = example.createCriteria();
        criteria.andLike("fullName", "%" + qo.getFullName() +"%");
        PageHelper.startPage(1, qo.size());
        List<User> users = userMapper.selectByExample(example);
        for(User user : users) {
            types.add(new Typeahead<Integer>(user.getId(), user.getFullName()));
        }
        return types;
    }

    /**
     * 用户注册
     * @param user 用户
     * @param roleIds 角色ids
     */
    @Transactional
    public void add(User user, List<Integer> roleIds) {
        UserMD5.createMd5Salt(user);
        user.setErrorTimes(0);
        user.setLoginTimes(0);
        user.setLocked(User.UNLOCK);
        user.setLockReason("");
        userMapper.insertSelective(user);
        if(roleIds != null && roleIds.size() > 0) {
            userMapper.addRoles(user.getId(), roleIds);
        }
    }
    /**
     * 修改用户资料 & 角色 <b>不包括密码</b>
     * @param user 用户
     * @param roleIds 角色ids
     * @return
     */
    @Transactional
    public int updateCommon(User user, List<Integer> roleIds) {
        int result = 0;
        user.setPassword(null); //资料修改不改变密码
        if (user.getLocked() == User.UNLOCK) {//解除锁定
            user.setLockReason("");
            user.setErrorContinues(0);
        }
        result += super.updateSelective(user); // 更新用户
        userMapper.deleteRoles(user.getId()); // 重置角色
        if(roleIds != null && roleIds.size() > 0) {
            userMapper.addRoles(user.getId(), roleIds);
        }
        return result;
    }

    /**
     * 修改用户密码 & 盐
     * @param user
     * @return
     */
    public int updatePassword(User user) {
        UserMD5.createMd5Salt(user);
        return userMapper.updateByPrimaryKeySelective(user);
    }

    public User getByUsername(String username) {
        return userMapper.getByUsername(username);
    }
    public int countByUsername(AuthQo qo) {
        if (StringUtil.isEmpty(qo.getUsername())) return 0; // 不传[username]不查询数据库
        return userMapper.countByUsername(qo);
    }

    public User doLogin(LoginUser loginUser) throws UnknownAccountException,LockedAccountException {
        User dbUser = userMapper.getByUsername(loginUser.getUsername());
        if (dbUser == null) {
            throw new UnknownAccountException("账号不存在");
        }
        // 如果数据库用户被锁定 直接结束
        if (User.LOCKED.equals(dbUser.getLocked())) {
            throw new LockedAccountException(dbUser.getLockReason());
        }

        Integer error = dbUser.getErrorContinues();
        if (error == null) error = 0; // 防止数据错误
        // 将登陆的密码明文转为md5
        String loginMd5 = UserMD5.doMd5(loginUser.getPassword(), dbUser.getSalt());

        if (ConfParamService.isOn("SHOW_LOGIN"))
            System.out.println(String.format("系统用户登陆=>账号：【%s】，密码明文：【%s】，MD5密文：【%s】 EQ 数据库密文【%s】",
                loginUser.getUsername(),loginUser.getPassword(),loginMd5,dbUser.getPassword()));

        if ( loginMd5.equals(dbUser.getPassword()) ) {
            dbUser.setLoginTimes(dbUser.getLoginTimes() + 1);
            dbUser.setErrorContinues(0);
            dbUser.setLocked(User.UNLOCK);
            dbUser.setLockReason("");
            dbUser.setSessionId(loginUser.getSessionId());
            dbUser.setHost(UserRealm.getHost());
            userMapper.updateByPrimaryKeySelective(dbUser);
        } else {
            error++;
            dbUser.setErrorContinues(error);
            dbUser.setErrorTimes(dbUser.getErrorTimes()+1);
            if (error >= maxError) {
                dbUser.setLocked(User.LOCKED);
                dbUser.setLockReason(String.format("密码连续错误【%s】次，账号被锁定，请联系管理员解锁。", maxError));
            }
            userMapper.updateByPrimaryKeySelective(dbUser);
            throw new IncorrectCredentialsException("用户名或密码错误");
        }
        return dbUser;
    }

    @Override
    protected void preAdd(User record) {
        super.preAdd(record);
        record.setId(null);
    }

    @Override
    protected User newInstanceT(Integer id) {
        return new User(id);
    }
}
